﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using JoshuaOneEight.Services;
using JoshuaOneEight.Windows;

namespace JoshuaOneEight
{
	/// <summary>
	/// The startup form of the program.
	/// </summary>
	public partial class MainForm : Form
	{
		#region Constructors

		/// <summary>
		/// Default constructor
		/// </summary>
		public MainForm()
		{
			InitializeComponent();
		}

		#endregion Constructors

		#region Private Members

		readonly LinkedList<TreeNode> mTreeLinkedList = new LinkedList<TreeNode>();
		private Verse mVerse;

		/// <summary>
		/// Check to see if a verse node is currently selected.
		/// </summary>
		private bool _IsVerseNodeSelected
		{
			get
			{
				return mTreeView.SelectedNode != null && mTreeView.SelectedNode.Level == 2;
			}
		}

		/// <summary>
		/// Tries to find the given node on the text or creates it if not found.
		/// </summary>
		/// <param name="nodes"></param>
		/// <param name="key"></param>
		/// <returns></returns>
		private static ThreeStateTreeNode _FindOrCreateNode( TreeNodeCollection nodes, string key )
		{
			ThreeStateTreeNode foundNode = null;

			// Try to find the node.
			foreach( TreeNode node in nodes )
			{
				if( node.Text == key )
				{
					foundNode = node as ThreeStateTreeNode;
					break;
				}
			}

			// Create the node if it is not found.
			if( foundNode == null )
			{
				foundNode = new ThreeStateTreeNode( key );
				nodes.Add( foundNode );
			}

			return foundNode;
		}

		/// <summary>
		/// Move to next verse nodes.
		/// </summary>
		/// <param name="forward"></param>
		private TreeNode _GetNextVerseNode( bool forward )
		{
			LinkedListNode<TreeNode> node = mTreeLinkedList.Find( mTreeView.SelectedNode );
			while( node != null )
			{
				node = forward ? node.Next : node.Previous;
				if( node != null && node.Value.Level == 2 )
				{
					return node.Value;
				}
			}

			return null;
		}

		/// <summary>
		/// Move to a random verse which has been memorized.
		/// </summary>
		private TreeNode _GetRandomVerseNode()
		{
			TreeNode returnNode = null;

			IEnumerable<TreeNode> memorizedVersesNodes =
				from node in mTreeLinkedList
				where node.Level == 2 && ((Verse)node.Tag).Memorized
				select node;

			int count = memorizedVersesNodes.Count();
			if( count > 0 )
			{
				int verseIndex = (new Random()).Next( 0, count - 1 );
				returnNode = memorizedVersesNodes.ElementAt( verseIndex );
			}

			return returnNode;
		}

		/// <summary>
		/// Handle the Click event of the Check Answer button.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleBtnCheckAnswerClick( object sender, EventArgs e )
		{
			mTxtVerse.Text = mTxtVerse.Text.TrimEnd();
			mTxtAnswer.Text = mVerse.Text.TrimEnd();

			// Check to see if the text is perfect.
			if( mTxtVerse.Text == mVerse.Text )
			{
				mLblAnswer.ForeColor = Color.Green;
				mLblAnswer.Text = "Perfect!";
			}
			// Check to see if only the character casing is different
			else if( string.Compare( mTxtVerse.Text, mVerse.Text, true ) == 0 )
			{
				mLblAnswer.ForeColor = Color.Blue;
				mLblAnswer.Text = "Correct, but casing is different";
			}
			// Check to see if the punctuation is different.
			else
			{
				// Remove any punctuation from the text entered by the user.
				StringBuilder s1 = new StringBuilder( mTxtVerse.Text.Length );
				foreach( char c in mTxtVerse.Text )
				{
					if( char.IsLetterOrDigit( c ) )
					{
						s1.Append( c );
					}
				}

				// Remove any punctuation from the master verse text.
				StringBuilder s2 = new StringBuilder( mVerse.Text.Length );
				foreach( char c in mVerse.Text )
				{
					if( char.IsLetterOrDigit( c ) )
					{
						s2.Append( c );
					}
				}

				// See if the user only mixed up punctuation.
				if( string.Compare( s1.ToString(), s2.ToString(), true ) == 0 )
				{
					mLblAnswer.ForeColor = Color.Blue;
					mLblAnswer.Text = "Correct, but punctuation is different";
				}
				else
				{
					mLblAnswer.ForeColor = Color.Red;
					mLblAnswer.Text = "Something is not quite right";
				}
			}

			// Get difference results and highlight the differences in the
			// answer input textbox.
			Diff diff1 = new Diff( mTxtVerse.Text, mVerse.Text );
			Diff.Result[] results1 = diff1.GetResults();
			mTxtVerse.Text = string.Empty;
			foreach( Diff.Result result in results1 )
			{
				mTxtVerse.SelectionColor = result.Matches ?
					SystemColors.ControlText : Color.Red;
				mTxtVerse.SelectedText = result.Letter.ToString();
			}

			// Get difference results and highlight the differences in the
			// answer result textbox.
			Diff diff2 = new Diff( mVerse.Text, mTxtVerse.Text );
			Diff.Result[] results2 = diff2.GetResults();
			mTxtAnswer.Text = string.Empty;
			foreach( Diff.Result result in results2 )
			{
				mTxtAnswer.SelectionColor = result.Matches ?
					SystemColors.ControlText : Color.Red;
				mTxtAnswer.SelectedText = result.Letter.ToString();
			}
		}

		/// <summary>
		/// Handle the Click event of the Learn Verse button.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleBtnLearnVerseClick( object sender, EventArgs e )
		{
			using( LearnForm form = new LearnForm() )
			{
				form.Verse = mVerse;
				form.ShowDialog();
			}
		}

		/// <summary>
		/// Handles the Click event of the File tool strip menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleFileToolStripMenuItemClick( object sender, EventArgs e )
		{
			mMnuEditVerse.Enabled = _IsVerseNodeSelected;
			mMnuDeleteVerse.Enabled = _IsVerseNodeSelected;
		}

		/// <summary>
		/// Handle the Click event of the About menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuAboutClick( object sender, EventArgs e )
		{
			using( AboutForm form = new AboutForm() )
			{
				form.ShowDialog();
			}
		}

		/// <summary>
		/// Handle the Click event of the Collapse All Groups menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuCollapseAllGroupsClick( object sender, EventArgs e )
		{
			mTreeView.CollapseAll();

			if( mTreeView.SelectedNode != null )
			{
				mTreeView.SelectedNode.EnsureVisible();
			}
		}

		/// <summary>
		/// Handle the Click event of the Collapse This Group menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuCollapseThisGroupClick( object sender, EventArgs e )
		{
			if( mTreeView.SelectedNode != null )
			{
				mTreeView.SelectedNode.Collapse();
			}
		}

		/// <summary>
		/// Handle the Click event of the Delete Verse menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuDeleteVerseClick( object sender, EventArgs e )
		{
			if( _IsVerseNodeSelected )
			{
				Verse verse = (Verse)mTreeView.SelectedNode.Tag;
				DialogResult result = MessageBox.Show( string.Format(
					"Are you sure you want to delete passage {0}?", verse.Passage ),
					"Delete", MessageBoxButtons.YesNo, MessageBoxIcon.Question,
					MessageBoxDefaultButton.Button2 );

				if( result == DialogResult.Yes )
				{
					DataContext.Instance.Verses.Remove( verse );
					DataContext.Instance.Save();
					_SetupTreeView();
				}
			}
		}

		/// <summary>
		/// Handle the Click event of the Edit Verse menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuEditVerseClick( object sender, EventArgs e )
		{
			if( _IsVerseNodeSelected )
			{
				using( EditForm form = new EditForm() )
				{
					form.Verse = (Verse)mTreeView.SelectedNode.Tag;
					DialogResult result = form.ShowDialog();
					if( result == DialogResult.OK )
					{
						DataContext.Instance.Save();
						_SetupTreeView();

					}
				}
			}
		}

		/// <summary>
		/// Handle the Click event of the Exit main menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuExitClick( object sender, EventArgs e )
		{
			Close();
		}

		/// <summary>
		/// Handle the Click event of the Expand All Groups menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuExpandAllGroupsClick( object sender, EventArgs e )
		{
			mTreeView.ExpandAll();

			if( mTreeView.SelectedNode != null )
			{
				mTreeView.SelectedNode.EnsureVisible();
			}
		}

		/// <summary>
		/// Handle the Click event of the Expand/Collapse Groups menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuExpandCollapseGroupsClick( object sender, EventArgs e )
		{
			if( mTreeView.SelectedNode != null )
			{
				mMnuExpandThisGroup.Enabled = !mTreeView.SelectedNode.IsExpanded
					&& mTreeView.SelectedNode.Nodes.Count > 0;
				mMnuCollapseThisGroup.Enabled = mTreeView.SelectedNode.IsExpanded;
			}
			else
			{
				mMnuExpandThisGroup.Enabled = false;
				mMnuCollapseThisGroup.Enabled = false;
			}
		}

		/// <summary>
		/// Handle the Click event of the Expand This Group menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuExpandThisGroupClick( object sender, EventArgs e )
		{
			if( mTreeView.SelectedNode != null )
			{
				mTreeView.SelectedNode.Expand();
				mTreeView.SelectedNode.EnsureVisible();
			}
		}

		/// <summary>
		/// Handle the Click event of the New Verse menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuNewVerseClick( object sender, EventArgs e )
		{
			using( EditForm form = new EditForm() )
			{
				form.Verse = new Verse();
				DialogResult result = form.ShowDialog();
				if( result == DialogResult.OK )
				{
					DataContext.Instance.Verses.Add( form.Verse );
					DataContext.Instance.Save();
					_SetupTreeView();
				}
			}
		}

		/// <summary>
		/// Handle the Click event of the Next Verse main menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuNextVerseClick( object sender, EventArgs e )
		{
			_MoveToNode( _GetNextVerseNode( true ) );
		}

		/// <summary>
		/// Handle the Click event of the Prev Verse main menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuPrevVerseClick( object sender, EventArgs e )
		{
			_MoveToNode( _GetNextVerseNode( false ) );
		}

		/// <summary>
		/// Handle the Click event of the Random Verse main menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuRandomVerseClick( object sender, EventArgs e )
		{
			_MoveToNode( _GetRandomVerseNode() );
		}

		/// <summary>
		/// Handle the Click event of the Navigation Pane menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuShowNavigationPaneClick( object sender, EventArgs e )
		{
			bool showNavigationPanel = mMnuShowNavigationPane.Checked;
			mMnuShowNavigationPane.Checked = !showNavigationPanel;
			splitContainer1.Panel1Collapsed = showNavigationPanel;
		}

		/// <summary>
		/// Handles the Click event of the Try Again menu item.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleMnuTryAgainClick( object sender, EventArgs e )
		{
			_ResetInput();
			mTxtVerse.Focus();
		}

		/// <summary>
		/// Handle the AfterSelect event of the Tree View.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleTreeViewAfterSelect( object sender, TreeViewEventArgs e )
		{
			// Control the input depending on what kind of node is selected.
			if( e.Node.Level == 2 )
			{
				mVerse = (Verse)e.Node.Tag;
				mLblPassage.Text = "Verse: " + mVerse.Passage;
				mLblAnswer.ForeColor = SystemColors.ControlText;
				mTxtVerse.Enabled = true;
				mBtnCheckAnswer.Enabled = true;
				mBtnLearnVerse.Enabled = true;
				mTreeView.ContextMenuStrip = mTreeViewMenu;
			}
			else
			{
				mVerse = null;
				mLblPassage.Text = "Verse: ";
				mTxtVerse.Enabled = false;
				mBtnCheckAnswer.Enabled = false;
				mBtnLearnVerse.Enabled = false;
				mTreeView.ContextMenuStrip = null;
			}

			// Always reset these items when a different node is selected.
			_ResetInput();
		}

		/// <summary>
		/// Handle the MouseClick event of the Tree View.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleTreeViewMouseClick( object sender, MouseEventArgs e )
		{
			ThreeStateTreeNode node = mTreeView.GetNodeAt( e.X, e.Y ) as ThreeStateTreeNode;

			// make sure that e.x is greater than the left edge of the checkbox,
			// make sure that e.x is less than the right edge of the checkbox.			
			if( node != null )
			{
				int left = (node.Level + 1) * mTreeView.Indent;
				if( left <= e.X && e.X <= left + mTreeView.ImageList.ImageSize.Width )
				{
					node.State = (node.State == CheckBoxState.Checked ?
						CheckBoxState.Unchecked : CheckBoxState.Checked);
					node.UpdateStateOfRelatedNodes();

					foreach( TreeNode categoryNode in mTreeView.Nodes )
					{
						foreach( TreeNode topicNode in categoryNode.Nodes )
						{
							foreach( TreeNode verseNode in topicNode.Nodes )
							{
								ThreeStateTreeNode vNode = verseNode as ThreeStateTreeNode;
								if( vNode != null )
								{
									Verse verse = (Verse)vNode.Tag;
									verse.Memorized = vNode.State == CheckBoxState.Checked;
								}
							}
						}
					}

					DataContext.Instance.Save();
				}
			}
		}

		/// <summary>
		/// Handle the Node Mouse Double Click event of the tree view.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleTreeViewNodeMouseDoubleClick( object sender, TreeNodeMouseClickEventArgs e )
		{
			mTxtVerse.Focus();
		}

		/// <summary>
		/// Handle the Enter event of the Answer textbox.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleTxtAnswerEnter( object sender, EventArgs e )
		{
			// Don't allow this field to have focus.
			mLblAnswer.Focus();
		}

		/// <summary>
		/// Handle the KeyDown event of the Verse textbox.
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void _HandleTxtVerseKeyDown( object sender, KeyEventArgs e )
		{
			// Check the answer when the user hit the enter key.
			if( e.KeyCode == Keys.Enter )
			{
				_HandleBtnCheckAnswerClick( mBtnCheckAnswer, EventArgs.Empty );
				e.Handled = true;
			}
		}

		/// <summary>
		/// Move to the specified node.
		/// </summary>
		/// <param name="node"></param>
		private void _MoveToNode( TreeNode node )
		{
			if( node != null )
			{
				_ResetInput();

				mTreeView.SelectedNode = node;
				mTreeView.SelectedNode.Expand();
				mTreeView.SelectedNode.EnsureVisible();

				mTxtVerse.Focus();
			}
		}

		/// <summary>
		/// Reset the input items.
		/// </summary>
		private void _ResetInput()
		{
			mLblAnswer.ForeColor = SystemColors.ControlText;
			mTxtVerse.SelectionColor = SystemColors.ControlText;
			mTxtAnswer.SelectionColor = SystemColors.ControlText;
			mLblAnswer.Text = "Answer: ";
			mTxtVerse.Text = string.Empty;
			mTxtAnswer.Text = string.Empty;
		}

		/// <summary>
		/// Create or repopulate the tree view from the data access object.
		/// </summary>
		private void _SetupTreeView()
		{
			// Set up the tree.
			mTreeView.Nodes.Clear();
			foreach( Verse verse in DataContext.Instance.Verses )
			{
				var categoryNode = _FindOrCreateNode( mTreeView.Nodes, verse.Category );
				var topicNode = _FindOrCreateNode( categoryNode.Nodes, verse.Topic );

				var verseNode = new ThreeStateTreeNode( verse.Passage );
				verseNode.Tag = verse;
				topicNode.Nodes.Add( verseNode );

				verseNode.State = (verse.Memorized ? CheckBoxState.Checked : CheckBoxState.Unchecked);
				verseNode.UpdateStateOfRelatedNodes();
			}

			// Copy the tree to a linked list so that we can navigate easier.
			mTreeLinkedList.Clear();
			LinkedListNode<TreeNode> lastNode = null;
			foreach( TreeNode categoryNode in mTreeView.Nodes )
			{
				lastNode = lastNode == null
					? mTreeLinkedList.AddFirst( categoryNode )
					: mTreeLinkedList.AddAfter( lastNode, categoryNode );

				foreach( TreeNode topicNode in categoryNode.Nodes )
				{
					lastNode = mTreeLinkedList.AddAfter( lastNode, topicNode );

					foreach( TreeNode verseNode in topicNode.Nodes )
					{
						lastNode = mTreeLinkedList.AddAfter( lastNode, verseNode );
					}
				}
			}
		}

		#endregion Private Members

		#region Protected Members

		/// <summary>
		/// Handle the OnLoad event of the Form.
		/// </summary>
		/// <param name="e"></param>
		protected override void OnLoad( EventArgs e )
		{
			base.OnLoad( e );

			// Load the tree data from a file.
			DataContext.Instance.Load();

			// Setup the tree view.
			_SetupTreeView();
		}

		#endregion Protected Members

	}
}
